#pragma once 

#include <pthread.h>
#include "Task.hpp"
#include <queue>

#define NUM 5

class ThreadPool{
  private:
  std::queue<Task> task_q;
  pthread_mutex_t lock;
  pthread_cond_t cond;
  static ThreadPool* inst;
  ThreadPool()
  {
    pthread_mutex_init(&lock,nullptr);
    pthread_cond_init(&cond,nullptr);
  }
  ThreadPool(const ThreadPool&)=delete;
  ThreadPool& operator=(const ThreadPool&)=delete; 
  public:
  static ThreadPool* GetInstance()
  {
    static pthread_mutex_t mtx=PTHREAD_MUTEX_INITIALIZER;
    if(inst==nullptr)
    {
      pthread_mutex_lock(&mtx);
      if(inst==nullptr)
      {
        inst=new ThreadPool;
        inst->Initthreadpool();
      }
      pthread_mutex_unlock(&mtx);
    }
    return inst;
  }

  void Threadwait()
  {
    pthread_cond_wait(&cond,&lock);
  }

  void Threadwakeup()
  {
    pthread_cond_signal(&cond);
  }

  void Lock()
  {
    pthread_mutex_lock(&lock);
  }

  void Unlock()
  {
    pthread_mutex_unlock(&lock);
  }

  bool Isempty()
  {
    return task_q.empty();
  }

  Task& Front()
  {
    return task_q.front();
  }

  void Pop()
  {
    return task_q.pop();
  }

  static void* ThreadRun(void* args)
  {
    ThreadPool* tp=(ThreadPool*)args;
    while(true)
    {
      tp->Lock();
      while(tp->Isempty())
      {
        tp->Threadwait();
      }
      Task& t=tp->Front();
      tp->Pop();
      tp->Unlock();
      t();
    }
  }

  void Initthreadpool()
  {
    for(int i=0;i<NUM;i++)
    {
      pthread_t tid;
      if(pthread_create(&tid,nullptr,ThreadRun,this)!=0)
      {
        LOG(FATAL,"create ThreadPool error");
      }
      pthread_detach(tid);
    }
    LOG(INFO,"Initthreadpool success");
  }

  void Pushtask(const Task &t)
  {
    Lock();
    task_q.push(t);
    Threadwakeup();
    Unlock();
  }
  ~ThreadPool()
  {
    pthread_mutex_destroy(&lock);
    pthread_cond_destroy(&cond);
  }
};
ThreadPool* ThreadPool::inst=nullptr;
